/**
 * Game Logic Thread
 * Purpose	Provide game logic for the game
 * 			Passes objects to OpenGL thread to draw
 * 			Gets touch events from OpenGL thread
 */
package com.unknown.explorer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.NavigableSet;
import java.util.TreeMap;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import javax.vecmath.GMatrix;
import javax.vecmath.GVector;
import javax.vecmath.Matrix3d;
import javax.vecmath.Matrix3f;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
import com.threed.jpct.Object3D; 
import com.threed.jpct.Primitives;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;
import com.threed.jpct.util.MemoryHelper;
import com.threed.jpct.util.Overlay;
import com.threed.jpct.util.SkyBox;
import com.threed.jpct.*;

import android.app.Activity;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Typeface;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.os.Message;
import android.view.Display;

public class gameLogic extends Thread implements Runnable, GLSurfaceView.Renderer{
	
	private static FrameBuffer fb = null;
	private int fps = 0;
	private long time = System.currentTimeMillis();
	private long time2 = System.currentTimeMillis();
	private RGBColor back = new RGBColor(50, 50, 100);

	public int count;
	public Handler h_gameLogic;
	private Handler h_ui;
	private static int limit = 10;
	
	private gameStatus gameStatusInstance;
	public static TreeMap<Object, Object> my3dObjects = new TreeMap<Object, Object>();
	
	private volatile World world = null;
	private static Camera cam;	
	private Light sun = null;
	
	private volatile static float touchTurn = 0;
	private volatile static float touchTurnUp = 0;
	private static float xpos = -1;
	private static float ypos = -1;
	
	private volatile static float touchTurnX = 0;
	private volatile static float touchTurnY = 0;
	
	//mapping of empty/non-empty/non-playable slots in the puzzle container
	//0 is empty, 1 is full, 2 is non-playable
	private int [][][] level;
	private int [][] topLayerStatus;
	private List<puzzlePiece> dropBlock;
	//private List<puzzlePiece> groundBlock;
//	private CopyOnWriteArrayList<puzzlePiece> groundBlock;
	
//	private int[][][] dropBlockMatrix;

	private final int puzzleSize = 10;
	private final int puzzleWidth = 8;
	private final int puzzleLength = 8;
	private final static int puzzleHeight = 12;
	
	private boolean nextBlock = true;
	
	private int pieceCount = 0;
	
	private puzzlePiece [][][] groundBlockMatrix;
	
	private Object worldLock = new Object();
	
	private Matrix3d rotationMatrixZ90 = new Matrix3d();
	private Matrix3d rotationMatrixZ270 = new Matrix3d();
	private Matrix3d rotationMatrixX90 = new Matrix3d();
	private Matrix3d rotationMatrixX270 = new Matrix3d();
	private Matrix3d rotationMatrixY90 = new Matrix3d();
	private Matrix3d rotationMatrixY270 = new Matrix3d();
	
	private static Object3D camObj;
	private List<Object3D> dropIndicator = new ArrayList<Object3D>();
	
	public TextureManager texManager;

	
	private static float cameraAngleZ = 0;
	
	private static SimpleVector cameraStartingPosition;
	
	private Activity parentActivity;
	
	//rotation control 2D overlay
	private Overlay rotateXOverlay;
	private Overlay rotateYOverlay;
	private Overlay rotateZOverlay;
	
	//joystick 2D overlay
	private Overlay joystickOverlay;
	private float joystickX;
	private float joystickY;
	private static boolean touchDrag = false;
	private static boolean joystickSelected = false;
	
	private SkyBox mySkyBox;
	
	private puzzleFactory pf;
	
	private AGLFont my2DFont;
	
	private int gameScore = 0;
	
	/**
	 * 
	 * @param h		message handler of the UI thread
	 */
	gameLogic(Handler h, Activity a){
		h_ui = h;
	
		h_gameLogic = new Handler_gameLogic();
		
		gameStatusInstance = new gameStatus();
		
		parentActivity = a;
		
		pf = new puzzleFactory();
		
		Paint paint = new Paint();
		paint.setAntiAlias(true);
		paint.setTypeface(Typeface.create((String)null, Typeface.BOLD));
		paint.setTextSize(22);
		
		my2DFont = new AGLFont(paint);
	}
	
	/**
	 * Get the game logic handler
	 * @return Handler for gameLogicThread
	 */
	public Handler getHandlerGameLogic()
	{
		if (h_gameLogic == null)
		{
			return null;
		}
		
		return h_gameLogic;
	}
	
	/**
	 * runnable function for the thread
	 */
	public void run() {
		while(true){
			loop();
		}
	}
	
	/**
	 * main game logic evaluation
	 */
	public void loop(){
		
		if(gameStatusInstance.gamePreInitialize == true)
		{
			
			texManager = TextureManager.getInstance();
			
			//sky box textures
			Texture temp = new Texture(parentActivity.getResources().openRawResource(R.raw.front));
			temp.compress();
			texManager.addTexture("front", temp);
			
			temp = new Texture(parentActivity.getResources().openRawResource(R.raw.back));
			temp.compress();
			texManager.addTexture("back", temp);
			
			temp = new Texture(parentActivity.getResources().openRawResource(R.raw.left));
			temp.compress();
			texManager.addTexture("left", temp);
			
			temp = new Texture(parentActivity.getResources().openRawResource(R.raw.right));
			temp.compress();
			texManager.addTexture("right", temp);
			
			temp = new Texture(parentActivity.getResources().openRawResource(R.raw.up));
			temp.compress();
			texManager.addTexture("up", temp);
			
			temp = new Texture(parentActivity.getResources().openRawResource(R.raw.down));
			temp.compress();
			texManager.addTexture("down", temp);
			
			//creation of sky box
			mySkyBox = new SkyBox(2500);
			world = mySkyBox.getWorld();
			mySkyBox.compile();
			
			//create rotation matrix
			rotationMatrixZ90.rotZ(Math.PI/2);
			rotationMatrixZ270.rotZ(Math.PI*3/2);
			rotationMatrixX90.rotX(Math.PI/2);
			rotationMatrixX270.rotX(Math.PI*3/2);
			rotationMatrixY90.rotY(Math.PI/2);
			rotationMatrixY270.rotY(Math.PI*3/2);
			
			texManager.addTexture("dropIndicator", new Texture(2,2,RGBColor.GREEN));
			RGBColor groundColor = new RGBColor(150,150,150,170);
			RGBColor pieceColor = new RGBColor(200,200,200,170);
			RGBColor pieceColorR = new RGBColor(200,0,0,170);
			RGBColor pieceColorG = new RGBColor(0,200,0,170);
			RGBColor pieceColorB = new RGBColor(0,0,200,170);
			RGBColor pieceColorY = new RGBColor(200,200,0,170);
			
			texManager.addTexture("ground", new Texture(32,32,groundColor));
			texManager.addTexture("puzzlePiece", new Texture(32,32,pieceColor));
			texManager.addTexture("puzzlePieceR", new Texture(32,32,pieceColorR));
			texManager.addTexture("puzzlePieceG", new Texture(32,32,pieceColorG));
			texManager.addTexture("puzzlePieceB", new Texture(32,32,pieceColorB));
			texManager.addTexture("puzzlePieceY", new Texture(32,32,pieceColorY));
			
			//rotation UI texture
			texManager.addTexture("rotateXControl", new Texture(parentActivity.getResources().openRawResource(R.raw.rotate_red), true));
			texManager.addTexture("rotateYControl", new Texture(parentActivity.getResources().openRawResource(R.raw.rotate_green), true));
			texManager.addTexture("rotateZControl", new Texture(parentActivity.getResources().openRawResource(R.raw.rotate_blue), true));
			
			//joystick UI texture
			texManager.addTexture("joytickRound", new Texture(parentActivity.getResources().openRawResource(R.raw.joystick_round), true));
			
			dropBlock = new ArrayList<puzzlePiece>();
			
			gameStatusInstance.gamePreInitialize = false;
		}
		
		if(gameStatusInstance.gameInitialize == true)
		{
			
			Collection<Object> c = my3dObjects.values();
			Iterator<Object> iii = c.iterator();
			
			synchronized(worldLock) {
				while(iii.hasNext() == true){		
					world.removeObject((Object3D) iii.next());
				}
			}
			dropBlock.clear();
			my3dObjects.clear();
			
			//x,y,z dimensions of the container
			level = new int[puzzleLength][puzzleWidth][puzzleHeight];
			
			groundBlockMatrix = new puzzlePiece[puzzleLength][puzzleWidth][puzzleHeight];

			//clear level
			for(int i = 0; i < level.length; i ++)
			{
				for(int j = 0; j < level[0].length; j ++)
				{
					for(int k = 0; k < level[0][0].length; k ++)
					{
						level[i][j][k] = 0;
						groundBlockMatrix[i][j][k] = null;
					}
				}
			}
			
			//set testing mapping for level
			for(int i = 0; i < level.length; i ++)
			{
				for(int j = 0; j < level[0].length; j ++)
				{
					if(i != 5 || j != 5)
					{
						level[i][j][0] = 1;
						puzzlePiece tt = new puzzlePiece();
						tt.name = "test"+i+j;
						tt.setX(i);
						tt.setY(j);
						tt.setZ(0);
						tt.obj3D = create("test"+i+j, "cube");
						rotatePermanent(tt.obj3D);
						tt.calculatePosition();
						world.addObject(tt.obj3D);
						groundBlockMatrix[i][j][0] = tt;
					}
					else
					{
						level[i][j][0] = 0;
						groundBlockMatrix[i][j][0] = null;
					}
				}
			}
			
			//initialize the top layer that is used for collision detection
			topLayerStatus = new int[puzzleLength][puzzleWidth];
			
			//clear level
			for(int i = 0; i < topLayerStatus.length; i ++)
			{
				for(int j = 0; j < topLayerStatus[0].length; j ++)
				{
					topLayerStatus[i][j] = 0;
				}
			}
				
			world.setAmbientLight(200, 200, 200);

			sun = new Light(world);
			sun.setIntensity(250, 250, 250);
			
			SimpleVector sv = new SimpleVector();
			sv.y -= 300;
			sv.z = 300;
			sun.setPosition(sv);

			cam = world.getCamera();
			camObj = Primitives.getCube(1);
			camObj.setTransparency(0);
			cameraStartingPosition = new SimpleVector(130, 130, 370);
			camObj.translate(cameraStartingPosition);
			cam.setPositionToCenter(camObj);
			SimpleVector look = new SimpleVector(0,0,100);
			cam.lookAt(look);
			cam.setFOV((float) 1.5);
					
			nextBlock = true;
			
			Display display = parentActivity.getWindowManager().getDefaultDisplay();
			Point size = new Point();
			display.getSize(size);
			int width = size.x;
			int height = size.y;
			
			rotateXOverlay = new Overlay(world, width*5/6 - 64, height/2 - 150 - 64, width*5/6 + 64, height/2 - 150 + 64, "rotateXControl");
			rotateXOverlay.setDepth(-1f);
			rotateXOverlay.setTransparency(255);
			my3dObjects.put("rotateXOverlay", rotateXOverlay.getObject3D());
			search("rotateXOverlay").setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
			
			rotateYOverlay = new Overlay(world, width*5/6 - 64, height/2 - 64, width*5/6 + 64, height/2 + 64, "rotateYControl");
			rotateYOverlay.setDepth(-2f);
			rotateYOverlay.setTransparency(255);
			my3dObjects.put("rotateYOverlay", rotateYOverlay.getObject3D());
			search("rotateYOverlay").setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
			
			rotateZOverlay = new Overlay(world, width*5/6 - 64, height/2 + 150 - 64, width*5/6 + 64, height/2 + 150 + 64, "rotateZControl");
			rotateZOverlay.setDepth(-3f);
			rotateZOverlay.setTransparency(255);
			my3dObjects.put("rotateZOverlay", rotateZOverlay.getObject3D());
			search("rotateZOverlay").setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
			
			joystickOverlay = new Overlay(world, width*1/6 - 64, height*4/6 - 64, width*1/6 + 64, height*4/6 + 64, "joytickRound");
			joystickOverlay.setDepth(-4f);
			joystickOverlay.setTransparency(255);
			my3dObjects.put("joystickOverlay", joystickOverlay.getObject3D());
			search("joystickOverlay").setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
			
			//save joystick position
			joystickX = width*1/6;
			joystickY = height*4/6;
			
			//ground creation
			Object3D hyg;
			hyg = create("grounding", "ground");			
			hyg.setTexture("ground");
			hyg.translate(0, 0, -puzzleSize);
			
			//add objects to the world
			Collection<Object> cc = my3dObjects.values();
			Iterator<Object> iy = cc.iterator();
			while(iy.hasNext() == true){		
				world.addObject((Object3D) iy.next());
			}
			
			gameStatusInstance.gameInitialize = false;
		}
	
		//meat of the game
breakGameOver: if(gameStatusInstance.gamePause == false && gameStatusInstance.gameOver == false)
		{	
			
			if(nextBlock == true)
			{
				//create next puzzle piece
				
				int randomPieceType = (int) (Math.floor(Math.random()*5));
				Object3D oo;
				int startPositionX;
				int startPositionY;
				puzzlePiece puzPiece;
				
				switch(randomPieceType)
				{
				
					case 1:	//2x2x1
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePiece");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						startPositionX = puzzleLength/2;
						startPositionY = puzzleWidth/2;
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePiece");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePiece");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY+1, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,1,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePiece");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY+1, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,1,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						break;
						
					case 2:	//2x2x2
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						startPositionX = puzzleLength/2;
						startPositionY = puzzleWidth/2;
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY+1, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,1,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY+1, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,1,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						startPositionX = puzzleLength/2;
						startPositionY = puzzleWidth/2;
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY, puzzleHeight + 4);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,0,1);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY, puzzleHeight + 4);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,0,1);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY+1, puzzleHeight + 4);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,1,1);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceR");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY+1, puzzleHeight + 4);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,1,1);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						break;
					
					case 3:	//L shaped
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceG");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						startPositionX = puzzleLength/2;
						startPositionY = puzzleWidth/2;
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceG");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceG");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						startPositionX = puzzleLength/2;
						startPositionY = puzzleWidth/2;
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+2, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(2,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceG");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY+1, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,1,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						break;
						
					case 4:	//Z shaped
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceB");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						startPositionX = puzzleLength/2;
						startPositionY = puzzleWidth/2;
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceB");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceB");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX-1, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(-1,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceB");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY+1, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,1,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceB");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX-1, startPositionY-1, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(-1,-1,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						break;
						
					default:	//4x1x1
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceY");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						startPositionX = puzzleLength/2;
						startPositionY = puzzleWidth/2;
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX-2, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(-2,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceY");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX-1, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(-1,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceY");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(0,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
						
						oo = create("piece"+ Integer.valueOf(pieceCount).toString(), "cube");
						oo.setTexture("puzzlePieceY");
						oo.setTransparency(122);
						pieceCount++;
						rotatePermanent(oo);
						puzPiece = new puzzlePiece();
						puzPiece.name = "piece"+ Integer.valueOf(pieceCount).toString();
						puzPiece.setInitializePosition(startPositionX+1, startPositionY, puzzleHeight + 3);
						puzPiece.obj3D = oo;
						puzPiece.setInitializeLocalPosition(1,0,0);
						puzPiece.calculatePosition();
						dropBlock.add(puzPiece);	
						world.addObject(oo);
				}
				
				//set flag off
				nextBlock = false;
			}
	
			//check and save top layer status
			for(int i = 0; i < level.length; i++)	//x axis
			{
				for(int j = 0; j < level[0].length; j++)	//y axis
				{
					for(int k = level[0][0].length - 1; k >= 0; k--)	//z axis
					{
						if(level[i][j][k] != 0)	//not empty
						{
							topLayerStatus[i][j] = k+1;	//save z value of empty slot	
							break;	// skip remaining z axis and check other x,y positions
						}
						else	//empty
						{
							if(k == 0){	//end of z axis check and still it's all empty
								topLayerStatus[i][j] = k;	//save z value of empty slot
							}
						}
					}
				}
			}
			
			TreeMap<Integer,Integer> layersCleared = new TreeMap<Integer,Integer>();
			
			//check if layers can be cleared
			con_nextLayer: for(int k = 0; k < level[0][0].length; k++)	//z axis
			{
				boolean clearable = true;	//flag for clearing each z axis layer
				
				for(int i = 0; i < level.length; i++)	//x axis
				{
					for(int j = 0; j < level[0].length; j++)	//y axis
					{
						if(level[i][j][k] != 1)
						{
							clearable = false;
							continue con_nextLayer;
						}
					}
				}
				if(clearable == true)
				{
					layersCleared.put(Integer.valueOf(k),Integer.valueOf(k));
					gameScore += 1;
				}
			}
			
			//clear specified layers
			NavigableSet<Integer> xx = layersCleared.descendingKeySet();
			Iterator<Integer> aa = xx.iterator();
			while(aa.hasNext())
			{
				int zval = aa.next();
				
				for(int i = 0; i < level.length; i++)	//x axis
				{
					for(int j = 0; j < level[0].length; j++)	//y axis
					{	
						// set level of this z axis to be empty and replace by layer above
						if(zval == puzzleHeight - 1)	// top layer
						{
							level[i][j][zval] = 0;
							
							puzzlePiece dd = groundBlockMatrix[i][j][zval];
							
							if(dd != null)
							{
								String s = (String) getKeyByValue(my3dObjects, dd.obj3D);
								if(s != null)
								{
									Object3D obj = delete(s);
									if(obj != null)
									{
										synchronized(worldLock) {
											world.removeObject(obj);
										}
									}
								}
							}
							
							groundBlockMatrix[i][j][zval] = null;
						}
						else
						{
							puzzlePiece dd = groundBlockMatrix[i][j][zval];
							
							if(dd != null)
							{
								String s = (String) getKeyByValue(my3dObjects, dd.obj3D);
								if(s != null)
								{
									Object3D obj = delete(s);
									if(obj != null)
									{
										synchronized(worldLock) {
											world.removeObject(obj);
										}
									}
								}
							}
							
//							//slide down layer on top of it
							for(int qq = zval; qq < puzzleHeight; qq++)
							{	
								if(qq < puzzleHeight-1)
								{
									level[i][j][qq] = level[i][j][qq + 1];
									groundBlockMatrix[i][j][qq] = groundBlockMatrix[i][j][qq + 1];
								}
								else
								{	//top layer set to 0/null
									level[i][j][qq] = 0;
									groundBlockMatrix[i][j][qq] = null;
								}
								
								dd = groundBlockMatrix[i][j][qq];
								if(dd != null)
								{
									dd.moveZ(-1);
									dd.calculatePosition();
								}
							}
						}
					}
				}
			}

			//drop puzzlepiece
			Iterator<puzzlePiece> ii = dropBlock.iterator();
			
			//get a mapping of the lowest z value of the dropping block
			List<puzzlePiece> puzzlePieceMap = new ArrayList<puzzlePiece>();
			
			//get the lowest z height for each x, y map in the dropping block
			continueHere: while(ii.hasNext())
			{
				puzzlePiece p = ii.next();
				
				Iterator<puzzlePiece> mm = puzzlePieceMap.iterator();
				while(mm.hasNext())
				{
					puzzlePiece q = mm.next();
					if(p.getX() == q.getX() && p.getY() == q.getY() && p.getZ() < q.getZ())
					{
						q.setZ(p.getZ());
						continue continueHere;
					}
				}
				// if it doesn't exit in the map, create a puzzle piece to map
				puzzlePiece pp = new puzzlePiece();
				pp.setX(p.getX());
				pp.setY(p.getY());
				pp.setZ(p.getZ());
				pp.obj3D = p.obj3D;
				pp.calculatePosition();
				puzzlePieceMap.add(pp);
			}
			
			//update translation from touch movement x, y axis
			int lowestX = puzzleLength - 1;
			int lowestY = puzzleWidth - 1;
			int highestX = 0;
			int highestY = 0;
			
			for(int g = 0; g < dropBlock.size(); g++)
			{
				int getValX= dropBlock.get(g).getX();
				int getValY= dropBlock.get(g).getY();
				
				if(getValX < lowestX)
				{
					lowestX = getValX;
				}
				if(getValX > highestX)
				{
					highestX = getValX;
				}
				
				if(getValY < lowestY)
				{
					lowestY = getValY;
				}
				if(getValY > highestY)
				{
					highestY = getValY;
				}
			}
			
			if(touchTurnX != 0 || touchTurnY != 0)
			{
					
				float tempTouchTurnX = touchTurnX*0.5f;
				float tempTouchTurnY = touchTurnY*0.5f;
				
				if(touchTurnX != 0)
					touchTurnX = 0;
				if(touchTurnY != 0)
					touchTurnY = 0;
				
				float newLowestX = lowestX + tempTouchTurnX;
				float newHighestX = highestX + tempTouchTurnX;
				
				float newLowestY = lowestY + tempTouchTurnY;
				float newHighestY = highestY + tempTouchTurnY;
				
				//x axis boundary check
				if(newLowestX < 0)
				{
					newLowestX = 0f;
				}
				else if( newLowestX  >= (float)puzzleLength)
				{
					newLowestX = (float)puzzleLength - 1f;
				}
				if(newHighestX < 0)
				{
					newHighestX = 0f;
				}
				else if(newHighestX >= (float) puzzleLength)
				{
					newHighestX = (float)puzzleLength - 1f;
				}
				
				//y axis boundary check
				if(newLowestY < 0)
				{
					newLowestY = 0f;
				}
				else if( newLowestY  >= (float)puzzleWidth)
				{
					newLowestY = (float)puzzleWidth - 1f;
				}
				if(newHighestY < 0)
				{
					newHighestY = 0f;
				}
				else if(newHighestY >= (float) puzzleWidth)
				{
					newHighestY = (float)puzzleWidth - 1f;
				}
	
				//calculate move distance
				int getSmallestMoveDistanceX = 0;
				int getSmallestMoveDistanceY = 0;
				
				if( (int)(Math.abs(newLowestX - lowestX)) < (int)(Math.abs(newHighestX - highestX)))
				{
					getSmallestMoveDistanceX = (int) (newLowestX - lowestX);
				}
				else
				{
					getSmallestMoveDistanceX = (int) (newHighestX - highestX);
				}
				
				if( (int)(Math.abs(newLowestY - lowestY)) < (int)(Math.abs(newHighestY - highestY)))
				{
					getSmallestMoveDistanceY = (int) (newLowestY - lowestY);
				}
				else
				{
					getSmallestMoveDistanceY = (int) (newHighestY - highestY);
				}
				
				
				//check to see if the move overlaps with other blocks that are on ground
				Iterator<puzzlePiece> uu = puzzlePieceMap.iterator();
				
				boolean lateralMove = true;
				
				while(uu.hasNext())
				{
					puzzlePiece n = uu.next();
					int newX = n.getX() + getSmallestMoveDistanceX;
					int newY = n.getY() + getSmallestMoveDistanceY;
					int lowestEmptyLayer = topLayerStatus[newX][newY];
					if(n.getZ() < lowestEmptyLayer)
					{
						lateralMove = false;
						break;
					}
				}
				
				if(lateralMove == true)
				{
					//apply move to block
					for(int g = 0; g < dropBlock.size(); g++)
					{
						dropBlock.get(g).moveX(getSmallestMoveDistanceX);
						dropBlock.get(g).moveY(getSmallestMoveDistanceY);
						dropBlock.get(g).calculatePosition();
					}
				}
			}
			
			Iterator<puzzlePiece> mm = puzzlePieceMap.iterator();
			
			boolean dropDown = true;
			//clear drop indicator
			Iterator<Object3D> gg = dropIndicator.iterator();
			
			synchronized(this.worldLock)
			{
				while(gg.hasNext())
				{
					world.removeObject(gg.next());
				}
			}
			
			int zEmptyIndex;
			Object3D dropIndicate;
			
			//iterate through puzzlePieceMap and see if collision is detected and add drop indicator
			synchronized(this.worldLock)
			{
				dropIndicator.clear();
				
				while(mm.hasNext())
				{
					puzzlePiece p = mm.next();
					
					//get the z index that is empty
					zEmptyIndex = topLayerStatus[p.getX()][p.getY()];
					
					//add colored square on the top layer directly beneath the dropBlock
					dropIndicate = Primitives.getBox(10, (float) 0.01);
					dropIndicate.setTexture("dropIndicator");

					if(zEmptyIndex > 0)
						dropIndicate.translate(p.getObject3DXPos(), p.getObject3DYPos(),(float) (zEmptyIndex*puzzleSize*2 - puzzleSize + 0.1));
					else
						dropIndicate.translate(p.getObject3DXPos(), p.getObject3DYPos(),(float) (0.1));
					rotatePermanent(dropIndicate);
					dropIndicator.add(dropIndicate);
					
					world.addObject(dropIndicate);
					
					//advance dropping if block z position is higher than toplayer z index
					if(p.getZ() <= zEmptyIndex)
					{
						dropDown = false;
					}
				}
			}
				
			//game tick
			if (System.currentTimeMillis() - time2 >= 400)
			{
				time2 = System.currentTimeMillis();
				
				if(dropDown == true)
				{
					//shift dropping block down 1 step in z axis
					ii = dropBlock.iterator();
					while(ii.hasNext())
					{	
						puzzlePiece p = ii.next();
						p.moveZ(-1);
						p.calculatePosition();
					}
				}
				else
				{
					// collision detect, transfer dropBlock to groundBlock
					ii = dropBlock.iterator();
					while(ii.hasNext())
					{	
						puzzlePiece p = ii.next();
						
						if(p.getZ() >= puzzleHeight)
						{
							gameStatusInstance.gameOver = true;
							break breakGameOver;
						}
						
						//fill in level 
						level[p.getX()][p.getY()][p.getZ()] = 1;
						p.calculatePosition();
						
						//gives blocks to groundBlock
						groundBlockMatrix[p.getX()][p.getY()][p.getZ()] = p;
					}
					//gives blocks to groundBlock and then empty dropBlock
					//groundBlock.addAll(dropBlock);
					dropBlock.clear();
					
					//signal to drop next piece
					nextBlock = true;
				}
			}//end of game tick
			
			
			if(xpos != -1 && ypos != -1)
			{
				SimpleVector vp = Interact2D.reproject2D3DWS(cam, fb, (int)xpos, (int)ypos).normalize();
			//	Logger.log("project vector : " + vp.x + ", " + vp.y + ", " + vp.z);
				
				Object[] res=world.calcMinDistanceAndObject3D(cam.getPosition(), vp, 10000 /*or whatever*/);
				
				if(res[1] != null)
				{
				//	Logger.log("distance: "+Float.toString((Float) res[0]));
					Object3D d = (Object3D)res[1];
					
					if(getKeyByValue(my3dObjects, d) == "joystickOverlay")
					{	
						joystickSelected = true;
//							touchTurnX = touchTurn;
//							touchTurnY = touchTurnUp;
					}
					
					else if(getKeyByValue(my3dObjects, d) == "rotateXOverlay")
					{
						// move drop block to x and y pos of touch grid
						ListIterator<puzzlePiece> iii = dropBlock.listIterator();
						while(iii.hasNext())
						{	
							puzzlePiece lll = iii.next();
							
							//transform if it passes boundary check
							lll.transformCoordinates(rotationMatrixX90);
							lll.calculatePosition();
							
							//check if it's out of bounds
							if(lll.getX() < 0 || lll.getX() >= puzzleLength
									|| lll.getY() < 0 || lll.getY() >= puzzleWidth
									|| lll.getZ() < 0 || lll.getZ() >= puzzleHeight)
							{
								//out of bounds, so revert to previous positions
								lll.revertPreviousPosition();
								lll.calculatePosition();
								
								while(iii.hasPrevious())
								{
									lll = iii.previous();
									lll.revertPreviousPosition();
									lll.calculatePosition();
								}
								break;
							}
						}
					}
					else if(getKeyByValue(my3dObjects, d) == "rotateYOverlay")
					{
						// move drop block to x and y pos of touch grid
						ListIterator<puzzlePiece> iii = dropBlock.listIterator();
						while(iii.hasNext())
						{	
							puzzlePiece lll = iii.next();
							
							//transform if it passes boundary check
							lll.transformCoordinates(rotationMatrixY90);
							lll.calculatePosition();
							
							//check if it's out of bounds
							if(lll.getX() < 0 || lll.getX() >= puzzleLength
									|| lll.getY() < 0 || lll.getY() >= puzzleWidth
									|| lll.getZ() < 0 || lll.getZ() >= puzzleHeight)
							{
								//out of bounds, so revert to previous positions
								lll.revertPreviousPosition();
								lll.calculatePosition();
								
								while(iii.hasPrevious())
								{
									lll = iii.previous();
									lll.revertPreviousPosition();
									lll.calculatePosition();
								}
								break;
							}
						}
					}
					else if(getKeyByValue(my3dObjects, d) == "rotateZOverlay")
					{
						// move drop block to x and y pos of touch grid
						ListIterator<puzzlePiece> iii = dropBlock.listIterator();
						while(iii.hasNext())
						{	
							puzzlePiece lll = iii.next();
							
							//transform if it passes boundary check
							lll.transformCoordinates(rotationMatrixZ90);
							lll.calculatePosition();
							
							//check if it's out of bounds
							if(lll.getX() < 0 || lll.getX() >= puzzleLength
									|| lll.getY() < 0 || lll.getY() >= puzzleWidth
									|| lll.getZ() < 0 || lll.getZ() >= puzzleHeight)
							{
								//out of bounds, so revert to previous positions
								lll.revertPreviousPosition();
								lll.calculatePosition();
								
								while(iii.hasPrevious())
								{
									lll = iii.previous();
									lll.revertPreviousPosition();
									lll.calculatePosition();
								}
								break;
							}
						}
					}
				}			
				ypos = -1;
				xpos = -1;
			}
			
			//translation control from joystick
			if(touchDrag == true && joystickSelected == true)
			{
				Logger.log("testing this part: " + Float.toString(touchTurn) + ", " + Float.toString(touchTurnUp));
				touchTurnX = touchTurn;
				touchTurnY = touchTurnUp;
			}

		}
		
		//game paused?
		int u;
		if(gameStatusInstance.gamePause == true)
			//pause code here
			u = 0;
		//game over?
		if(gameStatusInstance.gameOver == true)
		{
			//game over code here
			gameStatusInstance.gameInitialize = true;
			gameStatusInstance.gameOver = false;
			u = 0;
		}
	}
	
	/**
	 * 
	 * @param obj		Object3D to rotate
	 * @param rotate	SimpleVector rotation vector 
	 * @return			true if successful, false if unsuccessful
	 */
	public boolean rotate(Object3D obj, SimpleVector rotate)
	{
		if(rotate == null)
			return false;
		
		obj.clearRotation();
		obj.rotateX(rotate.x);
		obj.rotateY(rotate.y);
		obj.rotateZ(rotate.z);
		
		return true;
	}
	
	/**
	 * 
	 * @param obj		Object3D to translate
	 * @param translate	SimpleVector translation vector
	 * @return			true if successful, false is unsuccessful
	 */
	public boolean translate(Object3D obj, SimpleVector translate)
	{
		if(translate == null)
			return false;
		
		obj.clearTranslation();
		obj.translate(translate);	
		return true;
		
	}
	
	/**
	 * 
	 * @param name	name of the object
	 * @param type	object type to create(cube/ground)
	 * @return		Object3D of the created object, or null if not created
	 */
	public Object3D create(String name, String type){
		
		if(search(name) != null)
		{	//already exists
			return null;
		} 
	
		//create new entity
		if(type == "cube")
		{
			float[] f = new float[1];
			f[0] = (float)puzzleSize;
			Object3D newObj = pf.create("cube",f);
			my3dObjects.put(name, newObj);
			return newObj;
		}
		
		//create new entity
		if(type == "ground")
		{
//			Logger.log("im here!!!");
			float[] f = new float[2];
			f[0] = (float)(puzzleSize*puzzleLength);
			f[1] = (float) 0.01;
			Object3D newObj = pf.create("box", f);
//			Object3D newObj = Primitives.getBox((float)(puzzleSize*puzzleLength), (float) 0.01);
			rotatePermanent(newObj);
			my3dObjects.put(name, newObj);
			return newObj;
		}
		
		return null;
	}
	
	/**
	 * 
	 * @param objName	name of the 3D object to delete
	 * @return			Object3D if 3D object is deleted from map, null if object not found
	 */
	public Object3D delete(String objName)
	{
		Object3D obj = search(objName);
		if(obj != null)
		{
			my3dObjects.remove(objName);
			//success
			return obj;
		}
		else
		{
			//obj not found
			return null;
		}
	}
	
	/**
	 * 
	 * @param objName	name of 3D object to search for
	 * @return			Object3D is found, null if not found
	 */
	public static Object3D search(String objName)
	{
		Object3D newObj;
		newObj = (Object3D) my3dObjects.get(objName);
		if(newObj != null){
			return newObj;
		}
		else{
			return null;
		}
	}
	
	/**
	 * Handles messages
	 *
	 */
	static class Handler_gameLogic extends Handler{
		public void handleMessage(Message msg){
			gameMessage receivedMessage = (gameMessage)msg.obj;
			String msgType = receivedMessage.messageType;
			
			//currently using this to detect touch coordinates
			if(msgType == "touch turn")
			{
				touchTurn = (Float) receivedMessage.myData.get("touchTurn");
				touchTurnUp = -(Float) receivedMessage.myData.get("touchTurnUp");
				xpos = (Float) receivedMessage.myData.get("xpos");
				ypos = (Float) receivedMessage.myData.get("ypos");		
				
			}
			
			else if(msgType == "touch down")
			{				
				xpos = (Float) receivedMessage.myData.get("xpos");
				ypos = (Float) receivedMessage.myData.get("ypos");			
			}
			
			//currently using this to detect touch down or up
			else if(msgType == "touch updown")
			{
				String status = (String) receivedMessage.myData.get("touch status");
				if(status == "up")
				{
					touchDrag = false;
					joystickSelected = false;
				}
				else if(status == "down")
				{
					touchDrag = true;
				}
				
			}
			
			else if(msgType == "camera rotate")
			{
				cameraAngleZ = (Float) receivedMessage.myData.get("camera angle");
				
				Matrix3f rotM = new Matrix3f();
				rotM.rotZ((float) (cameraAngleZ/180*Math.PI));
				
				GMatrix rotGM = new GMatrix(3, 3);
				rotGM.set(rotM);
				
				SimpleVector sv = camObj.getTranslation();
				
				double[] doubArray = new double[3];
			
				doubArray[0] = cameraStartingPosition.x;
				doubArray[1] = cameraStartingPosition.y;
				doubArray[2] = cameraStartingPosition.z;
				
				GVector svb = new GVector(doubArray);
				
				GVector result = new GVector(3);
				result.mul(svb,rotGM);
				
				SimpleVector svResult = new SimpleVector(result.getElement(0), result.getElement(1), result.getElement(2));
				
				svResult.sub(sv);
				
				camObj.translate(svResult);
				
				cam.setPositionToCenter(camObj);
				SimpleVector look = new SimpleVector(0,0,100);
				cam.lookAt(look);
			}
		}
	}

	/**
	 * Rendering Thread
	 */
	public void onDrawFrame(GL10 gl) {
		
		if(world != null)
		{
			fb.clear(back);
			synchronized(worldLock) {
				if(mySkyBox != null)
					mySkyBox.render(world, fb);
				world.renderScene(fb);
				world.draw(fb);
				my2DFont.blitString(fb, "Layers Cleared: " + Integer.toString(gameScore), 50, 50, 255, RGBColor.WHITE);
			}
			fb.display();
			
			if (System.currentTimeMillis() - time >= 1000) {
				Logger.log(fps + "fps");
				fps = 0;
				time = System.currentTimeMillis();
			}
			fps++;	
		}
	}

	public void onSurfaceChanged(GL10 gl, int w, int h) {
		if (fb != null) {
			fb.dispose();
		}
		
		fb = new FrameBuffer(gl, w, h);

		MemoryHelper.compact();
		
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		// TODO Auto-generated method stub		
	}

	public static <T, E> T getKeyByValue(TreeMap<T, E> map, E value) {
	    for (Entry<T, E> entry : map.entrySet()) {
	        if (value.equals(entry.getValue())) {
	            return entry.getKey();
	        }
	    }
	    return null;
	}
	
	/**
	 * Permanent apply rotation
	 * @param o		Object3D to rotate
	 * @param m		optional, null for initialization rotation correction
	 */
	public void rotatePermanent(Object3D o, Matrix... m)
	{
		Matrix n;
		if(m.length != 1)
		{	
			n = new Matrix();
			n.rotateX((float) (Math.PI/2));
			n.rotateZ((float) (Math.PI/4));
		}
		else
		{
			n = m[0];
		}
		
		SimpleVector dir = new SimpleVector(1,1,0);
		SimpleVector up = new SimpleVector(0,0,1);
		
		o.setOrientation(dir, up);
	}
}
